/*************************************************
* Title：TinyToolKit
* Author：Opportunity 
* Describe：
* CreateTime：2020-05-06 22:12:24
* Version：1.0
* Modify Recorder：
*************************************************/

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using TinyToolKit.Timer;
using UnityEngine;

namespace TinyToolKit.Network
{
    public class UdpBroadcast
    {
        private Socket _udpBroadcast;
        private byte[] _dataBuffer;
        private const int BUFFER_SIZE = 1024;
        private Action<BroadcastPacket> _onBroadcastFeedback;
        
        private TTimerManager _timerManager;
        private int _timerId;
        private float _interval;
        private BroadcastPacket _broadcastPacket;

        public Socket socket => _udpBroadcast; 

        public UdpBroadcast(BroadcastPacket packet,float interval,Action<BroadcastPacket> onBroadcastFeedback)
        {
            _interval = interval < 0.001f ? 1 : interval;
            _onBroadcastFeedback = onBroadcastFeedback;
            _broadcastPacket = packet;
                
            _dataBuffer = new byte[BUFFER_SIZE];
            _timerManager = TinyTool.Instance.GetManager<TTimerManager>();
        }

        /// <summary>
        /// 开启UDP广播
        /// </summary>
        /// <returns>返回是否成功开启</returns>
        public bool StartUdpBroadcast()  
        {
            try
            {
                  _udpBroadcast = new Socket(AddressFamily.InterNetwork,SocketType.Dgram,ProtocolType.Udp);
                  _udpBroadcast.Bind(new IPEndPoint(IPAddress.Parse(_broadcastPacket.ip), 0));
                  _udpBroadcast.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.Broadcast,1);
                  _udpBroadcast.EnableBroadcast = true;
                  CreateBroadcastTimer(_broadcastPacket.port);

                  _udpBroadcast.BeginReceive(_dataBuffer, 0, 1024, SocketFlags.None, OnUdpReceiveMsg, _udpBroadcast);
                  return true;
            }
            catch (Exception e)
            {
                TLogTool.Error("【UdpBroadcast】广播消息时发生错误：" + e);
                return false;
            }
        }
        
        private void OnUdpReceiveMsg(IAsyncResult ar)
        {
            try
            {
                Socket socket = ar.AsyncState as Socket;
                if (socket != null && socket.Connected)
                {
                    int count = socket.EndReceive(ar);
                    if (count <= 0)
                    {
                        Close();
                        return;
                    }

                    string msg = Encoding.UTF8.GetString(_dataBuffer, 0, count);
                    string iep = socket.LocalEndPoint.ToString();
                    TLogTool.Info($"【UdpBroadcast】接收到<b>{iep}</b>发送的反馈消息：{msg}");
                    _udpBroadcast.BeginReceive(_dataBuffer, 0, 1024, SocketFlags.None, OnUdpReceiveMsg, _udpBroadcast);
                    
                    OnBroadcastComplete(msg);
                }
            }
            catch (Exception ex)
            {
                TLogTool.Error("【UdpBroadcast】接收消息时发生错误：" + ex);
            }
        }

        /// <summary>
        /// 关闭UDP广播
        /// </summary>
        public void Close()
        {
            try
            {
                DeleteBroadcastTimer();
                if (_udpBroadcast != null)
                {
                    _udpBroadcast.Close();
                    _udpBroadcast = null;
                    TLogTool.Info("【UdpBroadcast】关闭UDP广播消息");
                }
            }
            catch (Exception ex)
            {
                TLogTool.Error("【UdpBroadcast】关闭Udp时发生错误:" + ex);
            }
        }

        //广播消息后，接收到对方返回消息的回调方法
        private void OnBroadcastComplete(string msg)
        {
            BroadcastPacket packet = TinyUtility.Json.ToObject<BroadcastPacket>(msg);
            _onBroadcastFeedback?.Invoke(packet);
        }
        
        private void CreateBroadcastTimer(int port) 
        {
            IPEndPoint ipEndPoint = (IPEndPoint)_udpBroadcast.LocalEndPoint;
            BroadcastPacket packet = new BroadcastPacket
            {
                ip = ipEndPoint.Address.ToString(),
                port = ipEndPoint.Port,
                productName = _broadcastPacket.productName
            };
            
            byte[] data = Encoding.UTF8.GetBytes(TinyUtility.Json.ToJson(packet));
            SendBroadcastMsg(port, data);
            _timerId = _timerManager.AddTimerTask(() => {SendBroadcastMsg(port, data); }, _interval, 0);
        }
        
        /// <summary>
        /// 广播消息
        /// </summary>
        /// <param name="port">指定广播的端口</param>
        /// <param name="data">需要广播的数据</param>
        private void SendBroadcastMsg(int port,byte[] data)
        {
            try
            {
                //IPAddress.Broadcast 提供IP广播地址
                var iep = new IPEndPoint(IPAddress.Broadcast, port); 
                _udpBroadcast.SendTo(data, iep);
                
                TLogTool.Info($"【UdpBroadcast】向<b>{iep}</b>广播消息：" + Encoding.UTF8.GetString(data));
            }
            catch (Exception e)
            {
                TLogTool.Error("【UdpBroadcast】广播消息发生错误：" + e.Message);
            }
        }
        
        //关闭广播消息的计时器
        private void DeleteBroadcastTimer()
        {
            _timerManager.DeleteTimerTask(_timerId);
        }
    }
}
